#include "SenetHeuristicsSimetric.h"

#include <iostream>

// #define SAFE_SEQ 2
// #define STOP_SEQ 3

//////////////////////////////////////////////////////////////////////////
///				STATIC DECLARATIONS									   ///
//////////////////////////////////////////////////////////////////////////
// static inline void updateManhattanDistances(
// 	float* myManhattanDist,
// 	float* opponentMangattanDist,
// 	Definitions::PlayerColor currentSquareColor,
// 	Definitions::PlayerColor myColor,
// 	int numSquares, int currentSquare);

SenetHeuristicsSimetric::SenetHeuristicsSimetric() : 
		m_Owner(NULL),
		m_OwnerMovesPerStick(NULL),
		m_OpponentMovesPerStick(NULL),
		m_SenetBoard(NULL)
		{
		}
void SenetHeuristicsSimetric::setBoard(const SenetBoardDraft& board)
{
	m_SenetBoard = &(board);
}

void SenetHeuristicsSimetric::setOwner(const SenetPlayerExt* owner)
{
	m_Owner = owner;
}

// float SenetHeuristics::score2(const SenetBoardDraft& board)
// {
// 	// update the current board
// 	setBoard(board);
// 
// 	// first we test if this is a terminal board
// 	// if it is terminal then we give it either the highest or the lowest score
// 	// (according to the result)
// 	if (board.isTerminal())
// 	{
// 		return getTerminalScoring();
// 	}
// 
// 	// re-evaluate the board-dependent members
// 	calcBoardDependentMembers2();
// 
// 	// call the scoring sub-heuristics
// 
// 	// h1 - Manhattan distance
// 	float h1a = h1aScore();
// 	float h1b = h1bScore();
// 
// 	
// 
// 	float normalizedScore = h1a + h1b;
// 	if (normalizedScore < getLowerHeuristicBound() ||
// 		normalizedScore > getUpperHeuristicBound())
// 	{
// 		std::cout << "Heuristic returned value out of range!\n";
// 	}
// 	return normalizedScore;
// }

float SenetHeuristicsSimetric::score(const SenetBoardDraft& board)
{
	// update the current board
	setBoard(board);

	// first we test if this is a terminal board
	// if it is terminal then we give it either the highest or the lowest score
	// (according to the result)
	if (board.isTerminal())
	{
		return getTerminalScoring();
	}

	// re-evaluate the board-dependent members
	calcBoardDependentMembers2();
	
	// call the scoring sub-heuristics
	float scoreVector[HeuristicParams::numSubHeuristics][HeuristicParams::numPlayers];

	// h1 - Manhattan distance
	scoreVector[0][Ha] = h1aScore();
	scoreVector[0][Hb] = h1bScore();

	// h2 - Number of possible moves
	scoreVector[1][Ha] = h2aScore();
	scoreVector[1][Hb] = h2bScore();

	// h3 - Number of safe positions
	scoreVector[2][Ha] = h3aScore();
	scoreVector[2][Hb] = h3bScore(); 

	// h4 - Number of current pieces
	scoreVector[3][Ha] = h4aScore();
	scoreVector[3][Hb] = h4bScore();

	// h5 - Number of sequences of 2 (or more)
	scoreVector[4][Ha] = h5aScore();
	scoreVector[4][Hb] = h5bScore();

	// h6 - Number of sequences of 3 (or more)
	scoreVector[5][Ha] = h6aScore();
	scoreVector[5][Hb] = h6bScore();

	// h7 - Threat amount
	scoreVector[6][Ha] = h7aScore(); // me threating the opponent
	scoreVector[6][Hb] = h7bScore(); // the opponent threating me

	float normalizedScore = normalizeScoring(scoreVector);

	// TODO: REMOVE THIS - DEBUG
	if (normalizedScore < getLowerHeuristicBound() ||
		normalizedScore > getUpperHeuristicBound())
	{
		std::cout << "Heuristic returned value out of range!\n";
		// TODO: REMOVE THIS

		// h1 - Manhattan distance
		scoreVector[0][Ha] = h1aScore();
		scoreVector[0][Hb] = h1bScore();

		// h2 - Number of possible moves
		scoreVector[1][Ha] = h2aScore();
		scoreVector[1][Hb] = h2bScore();

		// h3 - Number of safe positions
		scoreVector[2][Ha] = h3aScore();
		scoreVector[2][Hb] = h3bScore(); 

		// h4 - Number of current pieces
		scoreVector[3][Ha] = h4aScore();
		scoreVector[3][Hb] = h4bScore();

		// h5 - Number of sequences of 2 (or more)
		scoreVector[4][Ha] = h5aScore();
		scoreVector[4][Hb] = h5bScore();

		// h6 - Number of sequences of 3 (or more)
		scoreVector[5][Ha] = h6aScore();
		scoreVector[5][Hb] = h6bScore();

		// h7 - Threat amount
		scoreVector[6][Ha] = h7aScore(); // me threating the opponent
		scoreVector[6][Hb] = h7bScore(); // the opponent threating me
	}
	// normalize and get the final score
	return normalizedScore;
}

// TODO: fix heuristics, so that positive is good for owner
float SenetHeuristicsSimetric::h1Score(bool isCheckingOwner)
{
	if (isCheckingOwner)
	{
		return (-1.0f * ((float)m_OwnerManhattanDistance)) / m_h1ManhattanMaxDistance;
	}
	else
	{
		return ((float)m_OpponentManhattanDistance / m_h1ManhattanMaxDistance);
	}
}

float SenetHeuristicsSimetric::h2Score(bool isCheckingOwner)
{
	if (isCheckingOwner)
	{
		return (getTotalPossibleMoves(isCheckingOwner)) / m_h2MaximumMovesNumber;
	}
	else
	{
		return (-1.0f * getTotalPossibleMoves(isCheckingOwner)) / m_h2MaximumMovesNumber;
	}
}

float SenetHeuristicsSimetric::h3Score(bool isCheckingOwner)
{
	float safePosNum = (float)m_SenetBoard->numSafeSquares();
	if (isCheckingOwner)
	{
		return ((float)m_OwnerSafePositionsNum) / safePosNum;
	}
	else
	{
		return ( -1.0f *(float)m_OpponentSafePositionsNum) / safePosNum;
	}
}

float SenetHeuristicsSimetric::h4Score(bool isCheckingOwner)
{
	if (isCheckingOwner)
	{
		return (-1.0f * (float)m_PositionsOwner.size()) / m_InitialPiecesNumber;
	}
	else
	{
		return ((float)m_PositionsOpponent.size()) / m_InitialPiecesNumber;
	}
}

float SenetHeuristicsSimetric::h5Score(bool isCheckingOwner)
{
	if (isCheckingOwner)
	{
		return ((float)m_NumSequence2Owner) / m_InitialPiecesNumberDiv2;
	}
	else
	{
		return (-1.0f *(float)m_NumSequence2Opponent) / m_InitialPiecesNumberDiv2;
	}
}

float SenetHeuristicsSimetric::h6Score(bool isCheckingOwner)
{
	if (isCheckingOwner)
	{
		return ((float)m_NumSequence3Owner) / m_InitialPiecesNumberDiv2;
	}
	else
	{
		return (-1.0f * (float)m_NumSequence3Opponent) / m_InitialPiecesNumberDiv2;
	}
}

float SenetHeuristicsSimetric::h7Score(bool isCheckingOwner)
{
	float totalThreat = (isCheckingOwner ? m_ThreatValueOwner : m_ThreatValueOpponent);
	if (isCheckingOwner)
	{
		return (totalThreat) / m_InitialPiecesNumber;
	}
	else
	{
		return (-1.0f *totalThreat) / m_InitialPiecesNumber;
	}
}



float SenetHeuristicsSimetric::getTotalPossibleMoves(bool isCheckingOwner)
{
	int totalMoves = 0;
	int sticksNumber = m_SticksNumber;
		//m_Owner->getNumSticks();
	vector<int>* moves = (isCheckingOwner ? m_OwnerMovesPerStick : m_OpponentMovesPerStick);

	for (int i = 1; i <= sticksNumber; ++i)
	{
		totalMoves += moves[i].size();
	}
	return (float)totalMoves;
}

void SenetHeuristicsSimetric::calcBoardDependentMembers2()
{
	// init

	// update positions
	m_PositionsOwner.clear();
	m_PositionsOpponent.clear();
	m_SenetBoard->getPiecesLocations(m_CurrentPlayerColor, m_PositionsOwner);
	m_SenetBoard->getPiecesLocations(m_OpponentPlayerColor, m_PositionsOpponent);
	CommonApps::sortVector(m_PositionsOwner);
	CommonApps::sortVector(m_PositionsOpponent);

	// get all the possible moves the players can make
	int sticks = m_SticksNumber;
		//m_Owner->getNumSticks();
	for (int i = 1; i <= sticks; i++)
	{
		m_OwnerMovesPerStick[i].clear();
		m_SenetBoard->generateMoves(m_OwnerMovesPerStick[i], m_CurrentPlayerColor, i);

		m_OpponentMovesPerStick[i].clear();
		m_SenetBoard->generateMoves(m_OpponentMovesPerStick[i], m_OpponentPlayerColor, i);
	}
	
	// update Manhattan distances
	m_OwnerManhattanDistance = getManhattanDistance(m_PositionsOwner);
	m_OpponentManhattanDistance = getManhattanDistance(m_PositionsOpponent);
	
	// calculate the number of safe positions for each player
	m_OwnerSafePositionsNum = getSafePositions(m_PositionsOwner);
	m_OpponentSafePositionsNum = getSafePositions(m_PositionsOpponent);

	// calculate the number of 2seq and 3seq for each player
	m_NumSequence2Owner = getSeqNum(m_PositionsOwner, 2);
	m_NumSequence2Opponent = getSeqNum(m_PositionsOpponent, 2);
	m_NumSequence3Owner = getSeqNum(m_PositionsOwner, 2);
	m_NumSequence3Opponent = getSeqNum(m_PositionsOpponent, 2);

	// calculate threat values
	m_ThreatValueOwner = getThreatAmount(true);

	

	//for (int i = 1; i <= sticks; i++)
	//{
	//	m_OwnerMovesPerStick[i].clear();
	//	m_SenetBoard->generateMoves(m_OwnerMovesPerStick[i], m_Owner->getPlayerColor(), i);

	//	m_OpponentMovesPerStick[i].clear();
	//	m_SenetBoard->generateMoves(m_OpponentMovesPerStick[i], m_Owner->getOpponentColor(), i);
	//}

	m_ThreatValueOpponent = getThreatAmount(false);


}


void SenetHeuristicsSimetric::init(const SenetPlayerExt* owner,
						   const SticksProbability& sticksProbability)
{
	m_Owner = owner;
	m_SticksProbability = &sticksProbability;
	m_SenetBoard = NULL;
	m_CurrentPlayerColor = m_Owner->getPlayerColor();
	m_OpponentPlayerColor = m_Owner->getOpponentColor();
	m_SticksNumber = owner->getNumSticks();
	m_InitialPiecesNumber = (float)m_Owner->getInitialNumPieces();
	m_InitialPiecesNumberDiv2 = (float)ceil((float)(m_Owner->getInitialNumPieces()) / 2.0f);
	m_InitialPiecesNumberDiv3 = (float)ceil((float)(m_Owner->getInitialNumPieces()) / 3.0f);

	m_OwnerMovesPerStick = new vector<int>[m_Owner->getNumSticks() + 1];
	m_OpponentMovesPerStick = new vector<int>[m_Owner->getNumSticks() + 1];

	calch1squaresMaxDistance(m_Owner->getSizeSquares(), m_Owner->getInitialNumPieces());
	calch2maximumMovesNumber(m_Owner->getSizeSquares(), m_Owner->getInitialNumPieces());

	// TODO: replace this with real heuristic params
	m_HeuristicParams.setSimpleCoefficients();
}
// 
// void SenetHeuristics::updateSafePositionList(const SenetBoard& initialBoard)
// {
// 	int numSquares = m_Owner->getSizeSquares();
// 	for (int i = 0; i < numSquares; i++)
// 	{
// 		const SenetBoard::SquareData& currentSquareData = initialBoard.getBoardState(i);
// 		if (currentSquareData.square_type == SenetBoard::SAFE)
// 		{
// 			m_SafePositions.push_back(i);
// 		}
// 	}
// }

void SenetHeuristicsSimetric::calch2maximumMovesNumber(int numSquares, int numPieces)
{
	m_h2MaximumMovesNumber = (float)(numSquares * numPieces);
}

void SenetHeuristicsSimetric::calch1squaresMaxDistance(int numSquares, int numPieces)
{
	m_h1ManhattanMaxDistance = (float)numSquares;
	for (int i = 1; i < numPieces; ++i)
	{
		m_h1ManhattanMaxDistance += (float)(numSquares - i);
	}
}

// bool SenetHeuristics::isCanMove(bool isCheckingOwner, int fromSquare, int steps)
// {
// 	if (steps > 0)
// 	{
// 		int destination = fromSquare + steps;
// 		if (isCheckingOwner)
// 		{
// 			if ((!CommonApps::hash_setContains(m_PositionsOwner, destination)) && // is the player landing on itself?
// 				(!CommonApps::hash_setContains(m_SafePositionsOpponent, destination)) && // is the player landing on an opponent safe position ?
// 				(!CommonApps::hash_setContains(m_Sequence2Opponent, destination)) && // is the player landing on an opponent sequence ?
// 				(!isJumpingBlockSequence(fromSquare, steps, m_Sequence3Opponent))) // is the player jumping over a block sequence ?
// 			{
// 				return true;
// 			}
// 		}
// 		else
// 		{
// 			if ((!CommonApps::hash_setContains(m_PositionsOpponent ,destination)) && // is the player landing on itself?
// 				(!CommonApps::hash_setContains(m_SafePositionsOwner, destination)) && // is the player landing on an opponent safe position ?
// 				(!CommonApps::hash_setContains(m_Sequence2Owner ,destination)) && // is the player landing on an opponent sequence ?
// 				(!isJumpingBlockSequence(fromSquare, steps, m_Sequence3Owner))) // is the player jumping over a block sequence ?
// 			{
// 				return true;
// 			}
// 		}
// 	}
// 	return false;
// }

// static inline void updateManhattanDistances(
// 	float* myManhattanDist,
// 	float* opponentMangattanDist,
// 	Definitions::PlayerColor currentSquareColor,
// 	Definitions::PlayerColor myColor,
// 	int numSquares, int currentSquare)
// {
// 	float currentDistance = (float)(numSquares - currentSquare);
// 	if (currentSquareColor == myColor)
// 	{
// 		*myManhattanDist += currentDistance;
// 	}
// 	else
// 	{
// 		*opponentMangattanDist += currentDistance;
// 	}
// }